package com.clf.common.util.util.lang;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Description: 模糊化工具.
 *
 * @author <a href="mailto:yrh@qtz360.com">yrh</a>
 * @Date Create on 2016/11/28
 * @since version1.0 Copyright 2018 QTZ All Rights Reserved.
 */
public class FuzzUtil {
    /**
     * 模糊化规则匹配模式
     */
    private static final Pattern RULE_PATTERN = Pattern.compile("(\\d|~|\\|)+");
    /**
     * 占位符号（正则元符号需要转义）
     */
    private static final String[] PLACEHOLDER_STRS = {"\\*", "#", "\\$"};
    private static char[] PLACEHOLDER_CHARS = chars();
    /**
     * 默认脱敏符号
     */
    private static final String DEFAULT_SIGN = "*";

    /**
     * 使用*号模糊化字符串.
     *
     * @param origin 原字符串
     * @param rule   模糊化规则
     * @return 模糊化后的字符串
     * @see #fuzz(String, String, String)
     */
    public static String fuzz(String origin, String rule) {
        return fuzz(origin, rule, DEFAULT_SIGN);
    }

    /**
     * 使用给定的符号模糊化字符串.
     * <p>
     * 模糊化规则说明：
     * 1. 指定需要模糊化处理的下标范围（范围符号使用“~”），例如：0~3，表示第1个到第4个字符。
     * 2. 下标从0开始，-1表示最后一个字符，例如：0~-1表示从第1个到最后1个字符。
     * 3. 一个下标，不需要使用“~”，例如：3，表示只模糊化第4个字符。
     * 4. 多个范围使用分隔符“|”，例如：0~3|-4~-1（等同于-4~3），表示前4个字符和最后4个字符。
     * 5. 字符串末尾的字符下标，用负数表示，例如：-2~-1，表示最后2个字符。
     *
     * @param origin      原字符串
     * @param rule        模糊化规则，格式：0~4|-4~-1|8
     * @param replacement 模糊化符号
     * @return 模糊化后的字符串
     */
    public static String fuzz(String origin, String rule, String replacement) {
        if (origin == null) {
            return null;
        }
        if (replacement == null) {
            throw new IllegalArgumentException("Replacement cannot be null!");
        }
        List<int[]> rules = parseRule(rule);
        if (CollectionUtils.isEmpty(rules)) {
            return origin;
        }

        char placeholder;
        int index = -1;
        if (replacement.length() == 1) {
            placeholder = replacement.charAt(0);
        } else {
            index = determinePlaceholder(origin);
            placeholder = PLACEHOLDER_CHARS[index];
        }
        int maxIndex = origin.length() - 1;
        StringBuffer originBuffer = new StringBuffer(origin);
        int start;
        int end;
        for (int[] range : rules) {
            start = range[0];
            end = range[1];
            if (start < 0) {
                start = maxIndex + start + 1;
            }
            if (end < 0) {
                end = maxIndex + end + 1;
            }
            if (start <= end) {
                replace(originBuffer, start, end, maxIndex, placeholder);
            } else {
                replace(originBuffer, start, maxIndex, maxIndex, placeholder);
                replace(originBuffer, 0, end, maxIndex, placeholder);
            }
        }
        if (index == -1) {
            return originBuffer.toString();
        }
        return originBuffer.toString().replaceAll(PLACEHOLDER_STRS[index], replacement);
    }

    /**
     * 从字符串数组初始化字符数组.
     *
     * @return
     */
    private static char[] chars() {
        char[] placeholders = new char[PLACEHOLDER_STRS.length];
        for (int i = 0; i < PLACEHOLDER_STRS.length; i++) {
            placeholders[i] = PLACEHOLDER_STRS[i].charAt(PLACEHOLDER_STRS[i].length() - 1);
        }
        return placeholders;
    }

    /**
     * 选择合适的占位符号.
     *
     * @param origin
     * @return
     */
    private static int determinePlaceholder(String origin) {
        for (int i = 0; i < PLACEHOLDER_CHARS.length; i++) {
            if (!origin.contains(String.valueOf(PLACEHOLDER_CHARS[i]))) {
                return i;
            }
        }
        throw new IllegalArgumentException("Can't find the right placeholder.");
    }

    /**
     * 将指定范围的字符替换为占位符号.
     *
     * @param originBuffer
     * @param start
     * @param end
     * @param placeholder
     */
    private static void replace(StringBuffer originBuffer, int start, int end, int maxIndex, char placeholder) {
        for (int i = start; i <= end && i <= maxIndex; i++) {
            originBuffer.setCharAt(i, placeholder);
        }
    }

    /**
     * 解析模糊化规则.
     *
     * @param rule 模糊化规则字符串
     * @return
     */
    private static List<int[]> parseRule(String rule) {
        Matcher matcher = RULE_PATTERN.matcher(rule);
        if (!matcher.find()) {
            throw new IllegalArgumentException("Fuzzy rules format error.");
        }
        List<int[]> ruleList = new ArrayList<>();
        String[] ranges = rule.split("\\|");
        String[] tmp;
        int[] values;
        for (String range : ranges) {
            if (StringUtils.isBlank(range)) {
                continue;
            }
            tmp = range.split("~");
            values = new int[2];
            if (tmp.length == 1) {
                values[0] = Integer.parseInt(tmp[0]);
                values[1] = values[0];
            } else if (tmp.length == 2) {
                values[0] = Integer.parseInt(tmp[0]);
                values[1] = Integer.parseInt(tmp[1]);
            } else {
                throw new IllegalArgumentException("Fuzzy rules of range error.");
            }
            ruleList.add(values);
        }
        return ruleList;
    }
}
